/************************************************************
 * Filename: WindowsSystem.cpp
 * Purpose: Manages the application's window, handles all
 * relevant messages from Windows.
 * Author: Jeremy Kings
 * Revision: 1 (20 November 2010)
 * Base: WindowsSystem.cpp (Chris Peters' GAM200 sample engine)
 *
 * Copyright 2010, Digipen Institute of Technology
 ************************************************************/
#include "Precompiled.h"
#include "WindowsSystem.h"
#include "GameStateMgr.h"
#include "DebugDrawer.h"

namespace Engine
{
	//WindowsSystem* pWinSystem = NULL; // pointer to Windows system for use in Windows message handling procedure

	//Used by windows to identify our window class type
	//(needed for registering/creating/unregistering the window)
	const char windowsClassName[] = "EngineWindowClass";

	//The message handling procedure for the game
	LRESULT WINAPI MessageHandler(HWND hWnd,	 //The window the message is for (ours in this case)
								  UINT msg,		 //The message type
								  WPARAM wParam, //The primary data for the message
								  LPARAM lParam) //The secondary data for the message (if any)
	{
		//See what type of message it is
		switch(msg)
		{
		case WM_CHAR: //A character key was pressed
			{
				// check if user requested restart
				switch(wParam)
				{
				case 'r':
					gGameStateNext = GS_RESTART;
					break;
				case 'd':
          #ifdef _DEBUG
					gDebugDrawEnabled = !gDebugDrawEnabled;
          #endif
					break;
				default:
					// Create a key message, set the character pressed (the wParam 
					// is the ascii value), then broadcast the message to all systems.
					Shout(&KeyDownMessage(wParam,MT_CharDown));
					break;
				}

				break;
			}
		case WM_LBUTTONDOWN:
			{
				//MouseButton m(MouseButton::LeftMouse,true,Vec2(WINDOWSSYSTEM->MousePosition.x,WINDOWSSYSTEM->MousePosition.y));
				//Shout(&m);
				break;
			}
		case WM_RBUTTONDOWN:
			{
				//MouseButton m(MouseButton::RightMouse,true,Vec2(WINDOWSSYSTEM->MousePosition.x,WINDOWSSYSTEM->MousePosition.y));
				//Shout(&m);
				break;
			}
		case WM_LBUTTONUP:
			{
				//MouseButton m(MouseButton::LeftMouse,false,Vec2(WINDOWSSYSTEM->MousePosition.x,WINDOWSSYSTEM->MousePosition.y));
				//Shout(&m);
				break;
			}
		case WM_RBUTTONUP:
			{
				//MouseButton m(MouseButton::RightMouse,false,Vec2(pWinSystem->MousePos.x,pWinSystem->MousePos.y));
				//Shout(&m);
				break;
			}
		case WM_MOUSEMOVE:
			{
				//pWinSystem->MousePos = MAKEPOINTS( lParam );
				//MouseMove m(Vector2D(pWinSystem->MousePos.x, pWinSystem->MousePos.y));
				//Shout(&m);
				break;
			}
		case WM_KEYDOWN: //A key was pressed
			// Create a key down message with the wParam
			// and broadcast the message to all systems.
			switch(wParam)
			{
			case VK_ESCAPE:
				gGameStateNext = GS_QUIT;
				break;
			default:
				Shout(&KeyDownMessage(wParam));
				break;
			}
			break;
		case WM_KEYUP: //A key was released
			//TODO: Handle any key logic you might need for game controls
			break;
		case WM_DESTROY: //A destroy message--time to kill the game
			//Make sure we shut everything down properly by telling Windows
			//to post a WM_QUIT message (the parameter is the exit code).
			PostQuitMessage(0);
			return 0;
		case WM_SYSKEYDOWN:
			{
				//Eat the WM_SYSKEYDOWN message to prevent freezing the game when
				//the alt key is pressed
				switch( wParam )
				{
				case VK_LMENU:
				case VK_RMENU:
					return 0;//Stop Alt key from stopping the winProc
				case VK_F4:
					//Check for Alt F4
					DWORD dwAltKeyMask = ( 1 << 29 );
					if( ( lParam & dwAltKeyMask ) != 0 )
						PostQuitMessage(0);
					return 0;
				}
				return 0;
			}
		}

		//We didn't completely handle the message, so pass it on for Windows to handle.
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}

	WindowsSystem::WindowsSystem(const char* windowTitle, int ClientWidth, int ClientHeight)
	{
		//Set the pointer to the windows system (used by message proc for reading mouse data)
		//pWinSystem = this;

		// The size passed to CreateWindow is the full size including the windows border and caption 
		// AdjustWindowRect will adjust the provided rect so that the client size of the window is the desired size
		RECT fullWinRect = {0, 0, ClientWidth, ClientHeight};
		AdjustWindowRect(&fullWinRect,	// rect for the full size of the window
						WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,// style of the window, which must match what is passed in to CreateWindow below
						FALSE);									// Does this window have a menu?

		//Register the window class for the game.
		WNDCLASSEX wc = {sizeof(WNDCLASSEX),	// size of this structure (passing the size allows Microsoft to update their interfaces and maintain backward compatibility)
			CS_CLASSDC,							// style of the window class--this is the base type (one device context for all windows in the process)
			MessageHandler,					// name of the message handling function
			0L, 0L,									// amount of extra memory to allocate for this class and window
			GetModuleHandle(NULL),				// handle to the instance that has the windows procedure--NULL means use this file.
			NULL,													// add an Icon as a resource and add them here
			LoadCursor(NULL, IDC_ARROW),	// use the default arrow cursor
			NULL, NULL,										// background brush and menu--these can be NULL
			windowsClassName, NULL};			// class name and the small icon (NULL just uses the default)

		RegisterClassEx(&wc);

		// Store the handle to the instance.
		hInstance = wc.hInstance;

		// Create the game's window.
		hWnd = CreateWindow(windowsClassName,	// class name
			windowTitle,								// name for the title bar
			WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,// style of the window (WS_BORDER, WS_MINIMIZEBOX, WS_MAXIMIZE, etc.)
			CW_USEDEFAULT, CW_USEDEFAULT,				// x and y pos of the window (screen coords for base windows, relative coords for child windows)
			fullWinRect.right-fullWinRect.left,	// width of the window, including borders
			fullWinRect.bottom-fullWinRect.top,	// height of the window, including borders and caption
			GetDesktopWindow(),					// parent window
			NULL,												// menu for the window
			hInstance,									// handle to the instance of the window (ignored in NT/2K/XP)
			NULL);											// lParam for the WM_CREATE message of this window
	}

	void WindowsSystem::ActivateWindow()
	{
		//Show the window (could also be SW_SHOWMINIMIZED, SW_SHOWMAXIMIZED, etc.)
		ShowWindow(hWnd, SW_SHOWDEFAULT);
		//Send a WM_PAINT message to the window
		UpdateWindow(hWnd);
	}

	//Process any windows messages and run the game until we get a quit message
	//While we don't use the window handle, in other cases we might want to only process messages for this window
	void WindowsSystem::Update(float dt)
	{
		MSG msg;
		// Look for any pending windows messages, remove them, then handle them.
		// It is important to get all windows messages available not just one -- don't want to miss anything critical.
		while (PeekMessage(&msg, // address of variable to store message in
			NULL,		// window handle--NULL just means get any message from the current thread
			0U,			// start message type to process
			0U,			// end message type to process
			PM_REMOVE)) // determines whether or not the message is removed
		{
			TranslateMessage(&msg);	//Makes sure WM_CHAR and similar messages are generated
			DispatchMessage(&msg);	//Calls the message procedure (see below) with this message

			//If we get a quit message, set the game state to quit
			if (msg.message == WM_QUIT)
				gGameStateNext = GS_QUIT;
		}
	}

	WindowsSystem::~WindowsSystem()
	{
		//pWinSystem = NULL;

		//Unregister the window class
		UnregisterClass(windowsClassName, hInstance);
	}
}